#pragma once

#include <functional>

// 模仿shared_ptr实现一个智能指针
template <typename T>
class smart_ptr
{
public:
	smart_ptr();
	//当构造函数前加上 explicit 关键字时，编译器不允许使用该构造函数进行隐式类型转换。
	//这意味着如果你有一个 smart_ptr<T> 的构造函数，如 smart_ptr sp = new T;
	//这样的代码会导致编译错误。只有在显式调用构造函数时，例如 smart_ptr<T> sp(new T);，
	//才会进行转换。

	//smart_ptr sp = new T;
	//在这行代码中，new T 生成了一个原始指针（T*），而 smart_ptr sp 则尝试将这个原始指针隐式转换
	//为 smart_ptr 类型。
	explicit smart_ptr(T *);
	smart_ptr(const smart_ptr &);
	smart_ptr(T *, std::function<void(T *)>);
	smart_ptr &operator=(const smart_ptr &);
	T &operator*() const;
	T *operator->() const;

	~smart_ptr();
	// 向bool的类型转换
	explicit operator bool() const;

	bool unique();
	void reset();
	void reset(T *);
	void reset(T *, std::function<void(T *)>);

	T *get() const;
	unsigned use_count();

private:
	// 默认的deleter
	static std::function<void(T *)> default_del;

private:
	unsigned *m_p_use_count = nullptr;
	T *m_pobject = nullptr;
	std::function<void(T *)> m_del = default_del;
};
// std::function<void(T*)>。
//这意味着 default_del 是一个可以接收一个指向类型 T 的指针并返回 void 的函数对象。
template <typename T>
std::function<void(T *)> smart_ptr<T>::default_del = [](T *p)
{delete p; p = nullptr; };

// Args... 是一个可变参数模板，允许传入任意数量和类型的参数。
//通过 new T(...) 创建类型为 T 的新对象，构造函数使用 std::forward<Args>(args)... 转发参数。
//这确保了参数的值类别（左值或右值）在传递过程中得到保留。
template <typename T, typename... Args>
smart_ptr<T> make_smart(Args &&...args)
{
	smart_ptr<T> sp(new T(std::forward<Args>(args)...));
	return sp;
}

template <typename T>
smart_ptr<T>::smart_ptr()
	: m_pobject(nullptr), m_p_use_count(new unsigned(1))
{
}

template <typename T>
smart_ptr<T>::smart_ptr(T *p)
	: m_pobject(p), m_p_use_count(new unsigned(1))
{
}

template <typename T>
smart_ptr<T>::smart_ptr(T *p, std::function<void(T *)> del)
	: m_pobject(p), m_p_use_count(new unsigned(1)), m_del(del)
{
}

template <typename T>
smart_ptr<T>::smart_ptr(const smart_ptr &rhs)
	: m_pobject(rhs.m_pobject), m_p_use_count(rhs.m_p_use_count), m_del(rhs.m_del)
{
	(*m_p_use_count)++;
}

template <typename T>
smart_ptr<T> &smart_ptr<T>::operator=(const smart_ptr &rhs)
{
	// 使用rhs的deleter
	m_del = rhs.m_del;
	// 递增右侧运算对象的引用计数
	++(*rhs.m_p_use_count);
	// 递减本对象的引用计数
	if (--(*m_p_use_count) == 0)
	{
		// 如果管理的对象没有其他用户了，则释放对象分配的成员
		m_del(m_pobject);

		delete m_p_use_count;
	}

	m_p_use_count = rhs.m_p_use_count;
	m_pobject = rhs.m_pobject;

	return *this; // 返回本对象
}

template <typename T>
T &smart_ptr<T>::operator*() const
{
	return *m_pobject;
}

template <typename T>
T *smart_ptr<T>::operator->() const
{
	return &this->operator*();
}

template <typename T>
smart_ptr<T>::~smart_ptr()
{
	if (--(*m_p_use_count) == 0)
	{
		m_del(m_pobject);
		m_pobject = nullptr;

		delete m_p_use_count;
		m_p_use_count = nullptr;
	}
}

template <typename T>
bool smart_ptr<T>::unique()
{
	return *m_p_use_count == 1;
}

template <typename T>
void smart_ptr<T>::reset()
{
	(*m_p_use_count)--;

	if (*m_p_use_count == 0)
	{
		m_del(m_pobject);
		delete m_p_use_count;
	}

	m_pobject = nullptr;
	// todo 为什么new unsigned(1)这里必须为1，而不能为0，空指针m_p_use_count不是应该为0吗？
	//为什么为0会导致内存泄漏呢？
	//todo shared_ptr的空指针的引用计数为0，而我们的为1
	m_p_use_count = new unsigned(1);
	//*m_p_use_count = 1;
	m_del = default_del;
}

template <typename T>
void smart_ptr<T>::reset(T *p)
{
	(*m_p_use_count)--;

	if (*m_p_use_count == 0)
	{
		m_del(m_pobject);
		delete m_p_use_count;
	}

	m_pobject = p;
	m_p_use_count = new unsigned(1);
	//*m_p_use_count = 1;
	m_del = default_del;
}

template <typename T>
void smart_ptr<T>::reset(T *p, std::function<void(T *)> del)
{
	reset(p);
	m_del = del;
}

template <typename T>
T *smart_ptr<T>::get() const
{
	return m_pobject;
}

//定义了一个名为 operator bool 的类型转换运算符
//类型转换运算符不需要显式地指定返回类型，因为它的返回类型是由其名称决定的。
//对于 operator bool()，编译器知道这个运算符将返回一个 bool 类型。
template <typename T>
smart_ptr<T>::operator bool() const
{
	return m_pobject != nullptr;
}

template <typename T>
unsigned smart_ptr<T>::use_count()
{
	return *m_p_use_count;
}
